﻿// <copyright file="Shape.cs" company="ThorIndustry">Copyright (c) ThorIndustry. All rights reserved.</copyright>

using System.Collections.Generic;

public partial class Shape
{
    public float Height = 0.1f;
    public float InnerDisplacement = 0.1f;

    private UnityEngine.Vector3[] allVertices;
    private UnityEngine.Vector2[] allPoints;

    // Let's assume the shape is a polygon with 4 vertices.
    // I have to generate a prime vertices for each existing one.
    // The prime vertice will be a little in the inner of the polygone and a little higher.
    // With that, I will be able to bevel the edge.
    // I will do that on both face.
    private UnityEngine.Mesh CreateExtrudedMesh()
    {
        List<int> triangles = new List<int>();

        int verticesSize = this.PuzzleShape.Vertices.Length;

        if (this.CanMove)
        {
            verticesSize *= 3;
        }
        else
        {
            verticesSize *= 2;
        }

        this.allVertices = new UnityEngine.Vector3[verticesSize];
        this.allPoints = new UnityEngine.Vector2[verticesSize];

        // Load normal vertices in the array.
        for (int index = 0; index < this.PuzzleShape.Vertices.Length; ++index)
        {
            this.allVertices[index] = this.PuzzleShape.Vertices[index];
            this.allPoints[index] = this.PuzzleShape.Points[index];
        }

        // Generate inner vertice for both side.
        this.GeneratePrimeVertices(!this.CanMove);

        this.GenerateTriangles(triangles, !this.CanMove);

        // Create the mesh.
        UnityEngine.Mesh mesh = new UnityEngine.Mesh();
        mesh.name = "ScriptedMesh";

        mesh.vertices = this.allVertices;
        mesh.triangles = triangles.ToArray();
        UnityEngine.Vector2[] uvs = new UnityEngine.Vector2[mesh.vertices.Length];

        float minX = this.PuzzleShape.Vertices[0].x;
        float maxX = this.PuzzleShape.Vertices[0].x;
        float minY = this.PuzzleShape.Vertices[0].y;
        float maxY = this.PuzzleShape.Vertices[0].y;

        for (int i = 1; i < this.PuzzleShape.Vertices.Length; i++)
        {
            UnityEngine.Vector3 vertice = this.PuzzleShape.Vertices[i];
            minX = UnityEngine.Mathf.Min(vertice.x, minX);
            maxX = UnityEngine.Mathf.Max(vertice.x, maxX);
            minY = UnityEngine.Mathf.Min(vertice.y, minY);
            maxY = UnityEngine.Mathf.Max(vertice.y, maxY);
        }

        this.MaxPoint = new UnityEngine.Vector2(maxX, maxY);
        this.MinPoint = new UnityEngine.Vector2(minX, minY);

        this.ShapeWidth = maxX - minX;
        this.ShapeHeight = maxY - minY;

        this.center.x = maxX - (this.ShapeWidth / 2);
        this.center.y = maxY - (this.ShapeHeight / 2);
        
        if (this.CanMove)
        {
            for (int index = 0; index < mesh.vertices.Length; index++)
            {
                uvs[index] = new UnityEngine.Vector2((mesh.vertices[index].x - minX) / (maxX - minX), (mesh.vertices[index].y - minY) / (maxY - minY));
            }
        }
        else
        {
            for (int index = 0; index < mesh.vertices.Length; index++)
            {
                if (index < this.PuzzleShape.Vertices.Length)
                {
                    uvs[index] = new UnityEngine.Vector2(1, 0);
                }
                else
                {
                    uvs[index] = new UnityEngine.Vector2(0, 0);
                }
            }
        }

        mesh.uv = uvs;

        mesh.RecalculateNormals();

        return mesh;
    }

    private void GeneratePrimeVertices(bool isSolution)
    {
        UnityEngine.Vector3 firstVector;
        UnityEngine.Vector3 secondVector;
        UnityEngine.Vector3 bisection;
        UnityEngine.Vector3 newVertice;
        int previousIndex = this.PuzzleShape.Vertices.Length - 1;
        int nextIndex = 1;
        for (int index = 0; index < this.PuzzleShape.Vertices.Length; ++index)
        {
            int frontIndex = index + this.PuzzleShape.Vertices.Length;

            // Get the vector from the previous point to the current.
            firstVector = this.PuzzleShape.Vertices[previousIndex] - this.PuzzleShape.Vertices[index];
            secondVector = this.PuzzleShape.Vertices[nextIndex] - this.PuzzleShape.Vertices[index];

            firstVector.Normalize();
            secondVector.Normalize();
                        
            bisection = (firstVector + secondVector).normalized;
            if (UnityEngine.Vector3.Cross(firstVector, secondVector).z < 0)
            {
                bisection = -bisection;
            }

            float sinAngle = UnityEngine.Mathf.Abs(UnityEngine.Vector3.Cross(bisection, firstVector).z);
           
            // Move the vertice a little inner.
            newVertice = this.PuzzleShape.Vertices[index] + (bisection * this.InnerDisplacement / sinAngle);
            newVertice.z = 0;
            
            this.allVertices[frontIndex] = new UnityEngine.Vector3(newVertice.x, newVertice.y, -this.Height);
            this.allPoints[frontIndex] = new UnityEngine.Vector2(newVertice.x, newVertice.y);

            if (!isSolution)
            {
                int backIndex = index + (this.PuzzleShape.Vertices.Length * 2);
                this.allVertices[backIndex] = new UnityEngine.Vector3(newVertice.x, newVertice.y, this.Height);
                this.allPoints[backIndex] = new UnityEngine.Vector2(newVertice.x, newVertice.y);
            }

            nextIndex = (nextIndex + 1) % this.PuzzleShape.Vertices.Length;
            previousIndex = (previousIndex + 1) % this.PuzzleShape.Vertices.Length;
        }
    }
    
    private void GenerateTriangleForBevel(int sideA, int sideB, int sideBPrime, int sideAPrime, List<int> triangles)
    {
        UnityEngine.Vector2[] points = new UnityEngine.Vector2[4];
        int[] pointIndex = new int[4];
        
        points[0] = this.allPoints[sideA];
        points[1] = this.allPoints[sideB];
        points[2] = this.allPoints[sideBPrime];
        points[3] = this.allPoints[sideAPrime];

        pointIndex[0] = sideA;
        pointIndex[1] = sideB;
        pointIndex[2] = sideBPrime;
        pointIndex[3] = sideAPrime;

        int[] triangleArray = Helpers.Triangulate(points);

        for (int index = 0; index < triangleArray.Length; ++index)
        {
            triangles.Add(pointIndex[triangleArray[index]]);
        }
    }

    private void GenerateTriangles(List<int> overallTriangles, bool solution = false)
    {
        List<int> oneSideTriangles = new List<int>();

        // Generate triangles for the front side.
        ////for (int index = 0; index < this.PuzzleShape.Triangles.Length; ++index)
        ////{
        ////    oneSideTriangles.Add(this.PuzzleShape.Triangles[index] + this.PuzzleShape.Vertices.Length);
        ////}
        UnityEngine.Vector2[] points = new UnityEngine.Vector2[this.PuzzleShape.Vertices.Length];
        for (int index = 0; index < this.PuzzleShape.Vertices.Length; ++index)
        {
            points[index] = this.allPoints[index + this.PuzzleShape.Vertices.Length];
        }

        oneSideTriangles.AddRange(Helpers.Triangulate(points));

        for (int index = 0; index < oneSideTriangles.Count; ++index)
        {
            oneSideTriangles[index] += this.PuzzleShape.Vertices.Length;
        }

        // Generate triangles for the front bevels.
        for (int index = 0; index < this.PuzzleShape.Vertices.Length; ++index)
        {
            int nextIndex = (index + 1) % this.PuzzleShape.Vertices.Length;
            this.GenerateTriangleForBevel(index, nextIndex, nextIndex + this.PuzzleShape.Vertices.Length, index + this.PuzzleShape.Vertices.Length, oneSideTriangles);
        }

        overallTriangles.AddRange(oneSideTriangles);

        if (!solution)
        {        
            // Compute triangle for the otherside.
            for (int index = 0; index < oneSideTriangles.Count; ++index)
            {
                if (oneSideTriangles[index] >= this.PuzzleShape.Points.Length)
                {
                    // This is a side dependant vertice.
                    oneSideTriangles[index] += this.PuzzleShape.Points.Length;
                }
            }

            oneSideTriangles.Reverse();
            overallTriangles.AddRange(oneSideTriangles);
        }
    }
}

